﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.Roster.ItemRoster
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using System;
using System.Collections;
using System.Collections.Generic;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.ObjectSystem;
using TaleWorlds.SaveSystem;

#nullable disable
namespace TaleWorlds.CampaignSystem.Roster
{
  public class ItemRoster : 
    IReadOnlyList<ItemRosterElement>,
    IEnumerable<ItemRosterElement>,
    IEnumerable,
    IReadOnlyCollection<ItemRosterElement>,
    ISerializableObject
  {
    [SaveableField(0)]
    private ItemRosterElement[] _data;
    [SaveableField(1)]
    private int _count;
    private static readonly List<ItemRoster> InstanceListForLoadGame = new List<ItemRoster>();

    internal static void AutoGeneratedStaticCollectObjectsItemRoster(
      object o,
      List<object> collectedObjects)
    {
      ((ItemRoster) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected virtual void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      collectedObjects.Add((object) this._data);
    }

    internal static object AutoGeneratedGetMemberValue_data(object o)
    {
      return (object) ((ItemRoster) o)._data;
    }

    internal static object AutoGeneratedGetMemberValue_count(object o)
    {
      return (object) ((ItemRoster) o)._count;
    }

    public ItemRosterElement this[int index] => this._data[index];

    public int Count => this._count;

    public ItemRoster()
    {
      this._data = new ItemRosterElement[4];
      this._count = 0;
    }

    public ItemRoster(ItemRoster other)
    {
      this._data = new ItemRosterElement[other.Count];
      this._count = 0;
      this.Add((IEnumerable<ItemRosterElement>) other);
      this.CalculateCachedStats();
    }

    [CachedData]
    public int VersionNo { get; private set; }

    private void EnsureLength(int length)
    {
      if (length <= 0 || this._data != null && length <= this._data.Length)
        return;
      int length1 = 4;
      if (this._data != null)
        length1 = this._data.Length * 2;
      ItemRosterElement[] itemRosterElementArray = new ItemRosterElement[length1];
      for (int index = 0; index < this._count; ++index)
        itemRosterElementArray[index] = this._data[index];
      this._data = itemRosterElementArray;
    }

    public int FindIndexOfItem(ItemObject item)
    {
      for (int indexOfItem = 0; indexOfItem < this._count; ++indexOfItem)
      {
        if (this._data[indexOfItem].EquipmentElement.Item == item)
          return indexOfItem;
      }
      return -1;
    }

    public int FindIndex(Predicate<ItemObject> predicate)
    {
      for (int index = 0; index < this._count; ++index)
      {
        if (predicate(this._data[index].EquipmentElement.Item))
          return index;
      }
      return -1;
    }

    public int FindIndexFirstAfterXthElement(Predicate<ItemObject> predicate, int x)
    {
      for (int firstAfterXthElement = 0; firstAfterXthElement < x + this._count; ++firstAfterXthElement)
      {
        if (predicate(this._data[firstAfterXthElement % this._count].EquipmentElement.Item))
          return firstAfterXthElement;
      }
      return -1;
    }

    public int FindIndexOfElement(EquipmentElement rosterElement)
    {
      for (int indexOfElement = 0; indexOfElement < this._count; ++indexOfElement)
      {
        if (rosterElement.IsEqualTo(this._data[indexOfElement].EquipmentElement))
          return indexOfElement;
      }
      return -1;
    }

    private int AddNewElement(ItemRosterElement rosterElement, bool insertAtFront = false)
    {
      int length = this._count + 1;
      this.EnsureLength(length);
      int index = this._count;
      this.OnRosterUpdated(ref rosterElement, rosterElement.Amount);
      if (insertAtFront)
      {
        for (int count = this._count; count > 0; --count)
          this._data[count] = this._data[count - 1];
        index = 0;
      }
      this._data[index] = rosterElement;
      this._count = length;
      this.UpdateVersion();
      return index;
    }

    public int AddToCounts(ItemObject item, int number)
    {
      return number == 0 ? -1 : this.AddToCounts(new EquipmentElement(item), number);
    }

    public int AddToCounts(EquipmentElement rosterElement, int number)
    {
      if (number == 0)
        return -1;
      int counts = this.FindIndexOfElement(rosterElement);
      if (counts < 0)
      {
        if (number < 0)
        {
          Debug.FailedAssert("Trying to delete an element from Item Roster that does not exist!", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Roster\\ItemRoster.cs", nameof (AddToCounts), 174);
          return -1;
        }
        counts = this.AddNewElement(new ItemRosterElement(rosterElement, 0));
      }
      this.OnRosterUpdated(ref this._data[counts], number);
      this._data[counts].Amount += number;
      if (this._data[counts].Amount <= 0)
      {
        this._data[counts] = this._data[this._count - 1];
        this._data[this._count - 1] = ItemRosterElement.Invalid;
        --this._count;
      }
      this.UpdateVersion();
      return counts;
    }

    public ItemRosterElement GetElementCopyAtIndex(int index)
    {
      if (index < this._count && index >= 0)
        return this._data[index];
      Debug.FailedAssert("GetElementCopyAtIndex can't find the element in ItemRoster", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Roster\\ItemRoster.cs", nameof (GetElementCopyAtIndex), 206);
      return ItemRosterElement.Invalid;
    }

    public ItemObject GetItemAtIndex(int index)
    {
      if (index < this._count && index >= 0)
        return this._data[index].EquipmentElement.Item;
      Debug.FailedAssert("Given index for GetItemAtIndex is not valid in ItemRoster", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Roster\\ItemRoster.cs", nameof (GetItemAtIndex), 217);
      return (ItemObject) null;
    }

    public int GetElementNumber(int index)
    {
      if (index < this._count && index >= 0)
        return this._data[index].Amount;
      Debug.FailedAssert("Given index for GetElementNumber is not valid in ItemRoster", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Roster\\ItemRoster.cs", nameof (GetElementNumber), 228);
      return 0;
    }

    public int GetElementUnitCost(int index)
    {
      if (index < this._count && index >= 0)
        return this._data[index].EquipmentElement.ItemValue;
      Debug.FailedAssert("Given index for GetElementUnitCost is not valid in ItemRoster", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Roster\\ItemRoster.cs", nameof (GetElementUnitCost), 239);
      return 0;
    }

    public int GetItemNumber(ItemObject item)
    {
      int indexOfItem = this.FindIndexOfItem(item);
      return indexOfItem >= 0 ? this._data[indexOfItem].Amount : 0;
    }

    public void Clear()
    {
      for (int index = 0; index < this._count; ++index)
      {
        this._data[index].Clear();
        this._data[index] = ItemRosterElement.Invalid;
      }
      this._count = 0;
      this.CalculateCachedStats();
      ItemRoster.RosterUpdatedEventDelegate rosterUpdatedEvent = this._rosterUpdatedEvent;
      if (rosterUpdatedEvent != null)
        rosterUpdatedEvent(new ItemRosterElement(), 0);
      this.UpdateVersion();
    }

    public static bool RostersAreIdentical(ItemRoster a, ItemRoster b)
    {
      if (a == b)
        return true;
      if (a == null || b == null || a.Count != b.Count)
        return false;
      for (int index = 0; index < a.Count; ++index)
      {
        ItemRosterElement elementCopyAtIndex1 = a.GetElementCopyAtIndex(index);
        ItemRoster itemRoster = b;
        EquipmentElement equipmentElement = elementCopyAtIndex1.EquipmentElement;
        ItemObject itemObject1 = equipmentElement.Item;
        int indexOfItem = itemRoster.FindIndexOfItem(itemObject1);
        if (indexOfItem == -1)
          return false;
        ItemRosterElement elementCopyAtIndex2 = b.GetElementCopyAtIndex(indexOfItem);
        equipmentElement = elementCopyAtIndex1.EquipmentElement;
        ItemObject itemObject2 = equipmentElement.Item;
        equipmentElement = elementCopyAtIndex2.EquipmentElement;
        ItemObject itemObject3 = equipmentElement.Item;
        if (itemObject2 == itemObject3 && elementCopyAtIndex1.Amount == elementCopyAtIndex2.Amount)
        {
          equipmentElement = elementCopyAtIndex1.EquipmentElement;
          ItemModifier itemModifier1 = equipmentElement.ItemModifier;
          equipmentElement = elementCopyAtIndex2.EquipmentElement;
          ItemModifier itemModifier2 = equipmentElement.ItemModifier;
          if (itemModifier1 == itemModifier2)
            continue;
        }
        return false;
      }
      return true;
    }

    public IEnumerator<ItemRosterElement> GetEnumerator()
    {
      for (int i = 0; i < this.Count; ++i)
        yield return this._data[i];
    }

    public int SelectRandomIndex(Func<ItemRosterElement, float> weightFunction)
    {
      List<(int, float)> valueTupleList = new List<(int, float)>();
      float num1 = 0.0f;
      for (int index = 0; index < this.Count; ++index)
      {
        float num2 = weightFunction(this._data[index]);
        if ((double) num2 > 9.9999997473787516E-06)
        {
          valueTupleList.Add((index, num2));
          num1 += num2;
        }
      }
      if (valueTupleList.Count <= 0)
        return -1;
      float num3 = MBRandom.RandomFloat * num1;
      float num4 = 0.0f;
      for (int index = 0; index < valueTupleList.Count; ++index)
      {
        num4 += valueTupleList[index].Item2;
        if ((double) num4 >= (double) num3)
          return valueTupleList[index].Item1;
      }
      return valueTupleList[valueTupleList.Count - 1].Item1;
    }

    IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();

    public IEnumerable<ItemRosterElement> RemoveIf(Func<ItemRosterElement, int> match)
    {
      List<ItemRosterElement> itemRosterElementList = new List<ItemRosterElement>();
      for (int index = 0; index < this._count; ++index)
      {
        int amount = match(this._data[index]);
        if (amount > 0)
        {
          if (amount > this._data[index].Amount)
            amount = this._data[index].Amount;
          itemRosterElementList.Add(new ItemRosterElement(this._data[index].EquipmentElement.Item, amount, this._data[index].EquipmentElement.ItemModifier));
          this.AddToCounts(this._data[index].EquipmentElement, -amount);
        }
      }
      return (IEnumerable<ItemRosterElement>) itemRosterElementList;
    }

    public void Add(IEnumerable<ItemRosterElement> rosterElementList)
    {
      foreach (ItemRosterElement rosterElement in rosterElementList)
        this.AddToCounts(rosterElement.EquipmentElement, rosterElement.Amount);
    }

    public void Add(ItemRosterElement itemRosterElement)
    {
      this.AddToCounts(itemRosterElement.EquipmentElement, itemRosterElement.Amount);
    }

    public void Remove(ItemRosterElement itemRosterElement)
    {
      this.AddToCounts(itemRosterElement.EquipmentElement, -itemRosterElement.Amount);
    }

    void ISerializableObject.SerializeTo(IWriter writer)
    {
      writer.WriteInt(this.Count);
      if (this._data != null)
      {
        writer.WriteInt(this._data.Length);
        foreach (ItemRosterElement itemRosterElement in this._data)
          writer.WriteSerializableObject((ISerializableObject) itemRosterElement);
      }
      else
        writer.WriteInt(0);
    }

    void ISerializableObject.DeserializeFrom(IReader reader)
    {
      this._count = reader.ReadInt();
      int length = reader.ReadInt();
      this._data = new ItemRosterElement[length];
      for (int index = 0; index < length; ++index)
        this._data[index] = (ItemRosterElement) reader.ReadSerializableObject();
      this.CalculateCachedStats();
    }

    private event ItemRoster.RosterUpdatedEventDelegate _rosterUpdatedEvent;

    public event ItemRoster.RosterUpdatedEventDelegate RosterUpdatedEvent
    {
      add => this._rosterUpdatedEvent += value;
      remove => this._rosterUpdatedEvent -= value;
    }

    public void UpdateVersion() => ++this.VersionNo;

    private void OnRosterUpdated(ref ItemRosterElement itemRosterElement, int count)
    {
      ItemObject itemObject = itemRosterElement.EquipmentElement.Item;
      this.TotalValue += itemObject.Value * count;
      if (itemObject.IsTradeGood)
        this.TradeGoodsTotalValue += itemObject.Value * count;
      if (itemObject.HasHorseComponent)
      {
        if (itemObject.HorseComponent.IsPackAnimal && itemRosterElement.EquipmentElement.ItemModifier == null)
          this.NumberOfPackAnimals += count;
        else if (itemObject.HorseComponent.IsMount && itemRosterElement.EquipmentElement.ItemModifier == null)
          this.NumberOfMounts += count;
        else if (itemObject.HorseComponent.IsLiveStock)
        {
          this.NumberOfLivestockAnimals += count;
          this.TotalFood += count * itemObject.HorseComponent.MeatCount;
        }
      }
      else
      {
        if (itemObject.IsFood)
        {
          this.TotalFood += count;
          if (itemRosterElement.Amount == 0 && count > 0)
            ++this.FoodVariety;
          else if (itemRosterElement.Amount > 0 && itemRosterElement.Amount + count <= 0)
            --this.FoodVariety;
        }
        this.TotalWeight += itemObject.Weight * (float) count;
      }
      ItemRoster.RosterUpdatedEventDelegate rosterUpdatedEvent = this._rosterUpdatedEvent;
      if (rosterUpdatedEvent == null)
        return;
      rosterUpdatedEvent(itemRosterElement, count);
    }

    [LoadInitializationCallback]
    public void OnLoadStarted(MetaData metaData) => ItemRoster.InstanceListForLoadGame.Add(this);

    public static void CalculateCachedStatsOnLoad()
    {
      foreach (ItemRoster itemRoster in ItemRoster.InstanceListForLoadGame)
      {
        ItemRoster.ReplaceInvalidItemsWithTrash(itemRoster);
        ItemRoster.RemoveZeroCountsFromRoster(itemRoster);
        itemRoster.CalculateCachedStats();
      }
      ItemRoster.InstanceListForLoadGame.Clear();
    }

    private static void RemoveZeroCountsFromRoster(ItemRoster itemRoster)
    {
      int index1 = 0;
      int index2 = 0;
      if (itemRoster._count != 0 && itemRoster._count > itemRoster._data.Length)
        itemRoster._count = itemRoster._data.Length;
      for (; index2 < itemRoster._count; ++index2)
      {
        ItemRosterElement itemRosterElement = itemRoster._data[index2];
        if (itemRosterElement.Amount > 0)
        {
          if (index1 != index2)
            itemRoster._data[index1] = itemRosterElement;
          ++index1;
        }
      }
      for (int index3 = index1; index3 < itemRoster._count; ++index3)
      {
        itemRoster._data[index3].Clear();
        itemRoster._data[index3] = ItemRosterElement.Invalid;
      }
      itemRoster._count = index1;
    }

    private static void ReplaceInvalidItemsWithTrash(ItemRoster itemRoster)
    {
      if (itemRoster._data == null)
        return;
      for (int index = itemRoster._data.Length - 1; index >= 0; --index)
      {
        ItemModifier itemModifier = itemRoster._data[index].EquipmentElement.ItemModifier;
        if ((itemModifier != null ? (!itemModifier.IsReady ? 1 : 0) : 0) != 0)
        {
          itemRoster.AddToCounts(itemRoster._data[index].EquipmentElement.Item, itemRoster._data[index].Amount);
          itemRoster._data[index].Clear();
        }
        ItemObject itemObject1 = itemRoster._data[index].EquipmentElement.Item;
        if ((itemObject1 != null ? (!itemObject1.IsReady ? 1 : 0) : 0) != 0)
        {
          ItemObject itemObject2 = itemRoster._data[index].EquipmentElement.Item;
          itemRoster.AddToCounts(DefaultItems.Trash, itemRoster._data[index].Amount);
          itemRoster._data[index].Clear();
          if (MBObjectManager.Instance.GetObject(itemObject2.Id) == itemObject2)
            MBObjectManager.Instance.UnregisterObject((MBObjectBase) itemObject2);
        }
      }
    }

    private void CalculateCachedStats()
    {
      this.TotalWeight = 0.0f;
      this.NumberOfPackAnimals = 0;
      this.NumberOfMounts = 0;
      this.NumberOfLivestockAnimals = 0;
      this.TotalValue = 0;
      this.TradeGoodsTotalValue = 0;
      this.TotalFood = 0;
      this.FoodVariety = 0;
      for (int index = 0; index < this.Count; ++index)
      {
        ItemObject itemObject = this._data[index].EquipmentElement.Item;
        int amount = this._data[index].Amount;
        this.TotalValue += itemObject.Value * amount;
        if (itemObject.IsTradeGood)
          this.TradeGoodsTotalValue += itemObject.Value * amount;
        if (itemObject.HasHorseComponent)
        {
          if (itemObject.HorseComponent.IsPackAnimal)
            this.NumberOfPackAnimals += amount;
          else if (itemObject.HorseComponent.IsMount)
            this.NumberOfMounts += amount;
          else if (itemObject.HorseComponent.IsLiveStock)
          {
            this.NumberOfLivestockAnimals += amount;
            this.TotalFood += amount * itemObject.HorseComponent.MeatCount;
          }
        }
        else
        {
          if (itemObject.IsFood)
          {
            this.TotalFood += amount;
            ++this.FoodVariety;
          }
          this.TotalWeight += itemObject.Weight * (float) amount;
        }
      }
    }

    [CachedData]
    public float TotalWeight { get; private set; }

    [CachedData]
    public int TotalFood { get; internal set; }

    [CachedData]
    public int FoodVariety { get; internal set; }

    [CachedData]
    public int TotalValue { get; internal set; }

    [CachedData]
    public int TradeGoodsTotalValue { get; internal set; }

    [CachedData]
    public int NumberOfPackAnimals { get; private set; }

    [CachedData]
    public int NumberOfLivestockAnimals { get; private set; }

    [CachedData]
    public int NumberOfMounts { get; private set; }

    public delegate void RosterUpdatedEventDelegate(ItemRosterElement item, int count);
  }
}
